/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithms;

import genotypes.Angles;
import genotypes.AnglesPopulation;
import genotypes.UnacceptableAnglesComparator;
import java.util.Arrays;
import objectives.ObjectiveFunction;
import operators.CrossoverOperator;
import operators.MutationOperator;
import operators.RankSelection;
import operators.SelectionOperator;
import operators.SplitOperator;

/**
 *
 * @author rusak
 */
public class IDEAStepByStep {

    private CrossoverOperator cOp;
    private MutationOperator mOp;
    private SplitOperator sOp;
    private ObjectiveFunction f;
    private SelectionOperator rOp;
    private int unacceptableSize;
    private int popSize;
    private double[][] limitations;
    private int anglesLength;
    private int howManyTimesMustITry = 100;

    public IDEAStepByStep(CrossoverOperator cOp, MutationOperator mOp, SplitOperator sOp,
            SelectionOperator rOp, ObjectiveFunction f, int popSize, int anglesLength,
            int unacceptableSize, double[][] limitations) {
        this.cOp = cOp;
        this.mOp = mOp;
        this.sOp = sOp;
        this.rOp = rOp;
        this.unacceptableSize = unacceptableSize;
        this.popSize = popSize;
        this.f = f;
        this.limitations = limitations;
        this.anglesLength = anglesLength;
        createNewPopulation();
    }
    private AnglesPopulation population;
    private AnglesPopulation descendants;
    private Angles pretendant;
    private Angles best;

    public Angles getBestSoFar() {
        return best;
    }

    public AnglesPopulation getRecentPopulation() {
        return population;
    }

    public void setChangedObjectiveFunction(ObjectiveFunction newF) {
        f = newF;
    }

    public void nextStep() throws Exception {



        try {
            if (best.getDistanceToDest()
                    > (pretendant = population.bestIndividual()).getDistanceToDest()) {
                best = pretendant;

            }
            descendants = cOp.crossover(population, limitations);
            descendants = mOp.mutate(descendants, limitations);
            descendants.evaluate(f);
            //if(best==null)

            if (best.getDistanceToDest()
                    > (pretendant = descendants.bestIndividual()).getDistanceToDest()) {
                best = pretendant;

            }
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
        AnglesPopulation splitted[] = sOp.split(population, descendants);
        AnglesPopulation unaccPop = splitted[1];
        AnglesPopulation accPop = splitted[0];
        int accPopTrials = 0;
        if (accPop.getSize() < popSize - unacceptableSize) {
            //za malo osobnikow dopuszczalnych! Losujemy od nowa populację.
            tryToCreateAcceptablePopulation();
        } else {
            accPop = rOp.select(accPop);
            unaccPop = rOp.select(unaccPop);

            Angles unacceptables[] = unaccPop.getPopulation();
            Arrays.sort(unacceptables, new UnacceptableAnglesComparator());

            int unaccBest = unaccPop.getSize();
            if (unaccBest > unacceptableSize) {
                unaccBest = unacceptableSize;
            }

            Angles bestUnacc[] = new Angles[unaccBest];

            for (int i = 0; i < unaccBest; i++) {
                bestUnacc[i] = unacceptables[i];
            }

            int accBest = popSize - unaccBest;
            population = AnglesPopulation.merge(accPop.bestIndividuals(accBest), bestUnacc);
            population.refreshAcceptability();
        }
    }
    //use only in dynamic problem!

    private void createNewPopulation() {
        population = AnglesPopulation.randomPopulation(anglesLength, popSize, limitations);
        best = Angles.randomAngles(anglesLength, limitations);

        best.setEvaluation(f.evaluate(best));
        population.evaluate(f);
    }

    private void tryToCreateAcceptablePopulation() throws Exception {
        int accPopTrials = 0;
        do {
            createNewPopulation();
            System.out.println("LEN: " + population.getAcceptable().length);
            System.out.println("NEED: " + (popSize - unacceptableSize));

            accPopTrials++;
        } while (accPopTrials < howManyTimesMustITry
                && population.getAcceptable().length < popSize - unacceptableSize);
        if (accPopTrials == howManyTimesMustITry) {
            throw new Exception("Too many random populations "
                    + "proved unacceptable!!");
        }

    }

    public void injectPopulation(AnglesPopulation population) {
        this.population = population;
        this.population.refreshAcceptability();
    }

    void retainAcceptablePopulation() throws Exception {
        if (population.getAcceptable().length < popSize - unacceptableSize) {
            tryToCreateAcceptablePopulation();
        }
    }
}
